package com.me.extend.ctx

import android.content.Intent
import android.os.*
import android.util.SparseArray
import com.me.extend.exception.unsupported
import java.io.Serializable

private val putIBinderMethod by lazy(LazyThreadSafetyMode.PUBLICATION) {
    Bundle::class.java.getMethod("putIBinder", String::class.java, IBinder::class.java).also {
        it.isAccessible = true
    }
}

internal fun Bundle.putBinderCompat(key: String, binder: IBinder) {
    putIBinderMethod.invoke(this, key, binder)
}

fun Bundle.put(key: String, value: Any?) {
    when (value) {
        is String -> putString(key, value)
        is IntArray -> putIntArray(key, value)
        is ShortArray -> putShortArray(key, value)
        is LongArray -> putLongArray(key, value)
        is ByteArray -> putByteArray(key, value)
        is FloatArray -> putFloatArray(key, value)
        is DoubleArray -> putDoubleArray(key, value)
        is BooleanArray -> putBooleanArray(key, value)
        is CharArray -> putCharArray(key, value)
        is CharSequence -> putCharSequence(key, value)
        is Bundle -> putBundle(key, value)
        is Array<*> -> putArray(key, value)
        is ArrayList<*> -> putArrayList(key, value)
        is SparseArray<*> -> putSparseArrayOfParcelable(key, value)
        is Binder -> if (Build.VERSION.SDK_INT >= 18) putBinder(
            key,
            value
        ) else putBinderCompat(key, value)
        is Parcelable -> putParcelable(key, value)
        is Serializable -> putSerializable(key, value) // Includes primitive types
        null -> putString(key, value) // Any non primitive type works for any null value
        else -> unsupported("Type ${value.javaClass.canonicalName} is not supported")
    }
}

private fun Bundle.putArray(key: String, value: Array<*>) {
    @Suppress("UNCHECKED_CAST")
    when {
        value.isArrayOf<CharSequence>() -> putCharSequenceArray(
            key,
            value as Array<out CharSequence>?
        )
        value.isArrayOf<String>() -> putStringArray(key, value as Array<String>?)
        value.isArrayOf<Parcelable>() -> putParcelableArray(key, value as Array<Parcelable>?)
        else -> unsupported("Array type ${value.javaClass.canonicalName} 不支持")
    }
}

private fun Bundle.putArrayList(key: String, value: ArrayList<*>) {
    @Suppress("UNCHECKED_CAST")
    when (value.firstOrNull()) {
        is CharSequence -> putCharSequenceArrayList(key, value as ArrayList<CharSequence>?)
        is String -> putStringArrayList(key, value as ArrayList<String>?)
        is Parcelable -> putParcelableArrayList(key, value as ArrayList<out Parcelable>?)
        is Int, null -> putIntegerArrayList(key, value as ArrayList<Int>?)
        else -> unsupported("Type ${value.first().javaClass.canonicalName} 不支持")
    }
}

private fun Bundle.putSparseArrayOfParcelable(key: String, value: SparseArray<*>) {
    @Suppress("UNCHECKED_CAST")
    putSparseParcelableArray(key, value as SparseArray<out Parcelable>?)
}

fun Intent.put(paramsArray: Array<out Pair<String, Any?>>) {
    paramsArray.forEach { params ->
        when (val value = params.second) {
            null -> putExtra(params.first, null as Serializable?)
            is Int -> putExtra(params.first, value)
            is Long -> putExtra(params.first, value)
            is CharSequence -> putExtra(params.first, value)
            is String -> putExtra(params.first, value)
            is Float -> putExtra(params.first, value)
            is Double -> putExtra(params.first, value)
            is Char -> putExtra(params.first, value)
            is Short -> putExtra(params.first, value)
            is Boolean -> putExtra(params.first, value)
            is Serializable -> putExtra(params.first, value)
            is Bundle -> putExtra(params.first, value)
            is Parcelable -> putExtra(params.first, value)
            is Array<*> -> when {
                value.isArrayOf<CharSequence>() -> putExtra(params.first, value)
                value.isArrayOf<String>() -> putExtra(params.first, value)
                value.isArrayOf<Parcelable>() -> putExtra(params.first, value)
                else -> unsupported("Intent extra ${params.first} has wrong type ${value.javaClass.name}")
            }
            is IntArray -> putExtra(params.first, value)
            is LongArray -> putExtra(params.first, value)
            is FloatArray -> putExtra(params.first, value)
            is DoubleArray -> putExtra(params.first, value)
            is CharArray -> putExtra(params.first, value)
            is ShortArray -> putExtra(params.first, value)
            is BooleanArray -> putExtra(params.first, value)
            else -> unsupported("Intent extra ${params.first} has wrong type ${value.javaClass.name}")
        }
    }
}